# include "Poly.h"
using namespace SymbolicMath;


namespace SymbolicMath{

	//For my use 
	Poly::Poly(vector<Monomial> polynomial1)
	{
		polynomial = polynomial1;
	}

	Poly::Poly()
	{
		Poly(0);
	}
	//For double Poly's 
	Poly::Poly(double integer)
	{
		Monomial temp;
		temp.coeff = integer;
		temp.power = 0;
		if(integer != 0)
		{
			polynomial.push_back(temp);
		}
	}

	//Reading a string of polynomials
	Poly::Poly(string inputString)
	{
		//Temp Variables
		vector<Monomial> polynomialTemp;
		istringstream streamInput(inputString);
		char tempChar;
		
		//Loop to read through buffered reader
		while(streamInput.get(tempChar))
		{
			Monomial temp; //temp for each Monomial
			bool negative = false; //a sign indicator for coeff
			double coeff = 0; //coeffcient value to be placed in Monomial temp
			int power = 0; // power value to be placed in monimial temp

			// Checks if its a negative 
			if(tempChar == '-')
			{
				negative = true;
				streamInput.get(tempChar);
			}
			// I don't need this in the first iteration but after that i will need it
			else if (tempChar == '+')
			{
				negative = false;
				streamInput.get(tempChar);
			}
			
			//Checks for an x
			if(tempChar == 'x')
			{
				if (negative == true)
				{
					coeff = -1;
				}
				else 
				{
					coeff = 1;
				}
				if(streamInput.peek() == '^')
				{
					streamInput.get();
					streamInput >> power;
				}
				else
				{
					power = 1;
				}
			}
			//Checks for a number
			else if(isdigit(tempChar))
			{
				streamInput.putback(tempChar);
				if(negative == true)
				{
					streamInput >> coeff;
					coeff = -coeff;
				}
				else
				{
					streamInput >> coeff;
				}
				if(streamInput.peek() == 'x')
				{
					streamInput.get();
					if(streamInput.peek() == '^')
					{
						streamInput.get();
						streamInput >> power;
					}
					else
					{
						power = 0;
					}
				}
			}
			if(coeff != 0)
			{
				temp.coeff = coeff;
				temp.power = power;
				polynomialTemp.push_back(temp);
			}
		}
		//Change orientation of polynomial from ascending order to descending order
		int size = polynomialTemp.size();
		while(size > 0)
		{
			polynomial.push_back((Monomial)polynomialTemp.back());
			polynomialTemp.pop_back();
			size--;
		}	
	};

	// Overwriting multiplication operator
	const Poly & Poly::operator *=(const Poly & rhs)
	{	
		//Used loop through go through each monomial from lhs * rhs polynomial
		vector<Monomial> rhsVector = rhs.polynomial;
		vector<Monomial> tempVector;
		Poly result;
		Monomial temp;
		int rhsSize = rhsVector.size();
		int lhsSize = polynomial.size();
		for(int i = 0; i < lhsSize; i++)
		{
			for(int j = 0; j < rhsSize; j++)
			{
				double coeff = 0;
				int power = 0;
				coeff = polynomial[i].coeff * rhsVector[j].coeff;
				power = polynomial[i].power + rhsVector[j].power;
				temp.coeff = coeff;
				temp.power = power;
				tempVector.push_back(temp);
			}
			result += Poly(tempVector);
			tempVector.clear();
		}
		polynomial = result.polynomial;
		return *this;
	}

	//Used the highest power and worked my way down and only added if 
	// coefficeint is greater then 0
	const Poly & Poly::operator +=(const Poly & rhs)
	{
		if(polynomial.size() == 0)
		{
			polynomial = rhs.polynomial;
			return *this;
		}
		int lhsCounter;
		int rhsCounter;
		int highestPower;
		vector <Monomial> rhsVector = rhs.polynomial;
		vector <Monomial> result;
		Monomial lhsMonomial = polynomial.back();
		Monomial rhsMonomial = rhsVector.back();
		if(lhsMonomial.power > rhsMonomial.power)
		{
			highestPower = lhsMonomial.power;
		}
		else 
		{
			highestPower = rhsMonomial.power;
		}
		for(int counter = 0; counter <= highestPower; counter++)
		{
			Monomial temp;
			lhsCounter = -1;
			rhsCounter = -1;
			for(int i = 0; i < (int)polynomial.size(); i++)
			{
				if(polynomial[i].power == counter)
				{
					lhsCounter = i;
					break;
				}
			}
			for(int j = 0; j < (int)rhsVector.size(); j++)
			{
				if(rhsVector[j].power == counter)
				{
					rhsCounter = j;
					break;
				}
			}

			if((lhsCounter != -1) && (rhsCounter != -1))
			{
				temp.power = counter;
				temp.coeff = (int)polynomial[lhsCounter].coeff + (int)rhsVector[rhsCounter].coeff;
				if(temp.coeff != 0)
				{
					result.push_back(temp);
				}
			}
			else if((lhsCounter != -1) && (rhsCounter == -1))
			{
				result.push_back(polynomial[lhsCounter]);
			}
			else if((rhsCounter != -1) && (lhsCounter == -1))
			{
				result.push_back(rhsVector[rhsCounter]);
			}
		}
		polynomial = result;
			
	 return *this;
	}

	//Just plugged it in and solved used pow function
	double Poly::operator ()(double value)
	{
		double result = 0;
		Monomial temp;
		vector<Monomial> tempVector = polynomial;
		while(tempVector.size() > 0)
		{
			temp = tempVector.back();
			result += temp.coeff * pow(value, temp.power);
			tempVector.pop_back();
		}
		return result;
	}

	//identical to + with signs changed
	const Poly & Poly::operator -=(const Poly & rhs)
	{
		int lhsCounter;
		int rhsCounter;
		int highestPower;
		vector <Monomial> rhsVector = rhs.polynomial;
		vector <Monomial> result;
		Monomial lhsMonomial = polynomial.back();
		Monomial rhsMonomial = rhsVector.back();
		if(lhsMonomial.power > rhsMonomial.power)
		{
			highestPower = lhsMonomial.power;
		}
		else 
		{
			highestPower = rhsMonomial.power;
		}
		for(int counter = 0; counter <= highestPower; counter++)
		{
			Monomial temp;
			lhsCounter = -1;
			rhsCounter = -1;
			for(int i = 0; i < (int)polynomial.size(); i++)
			{
				if(polynomial[i].power == counter)
				{
					lhsCounter = i;
					break;
				}
			}
			for(int j = 0; j < (int)rhsVector.size(); j++)
			{
				if(rhsVector[j].power == counter)
				{
					rhsCounter = j;
					break;
				}
			}

			if((lhsCounter != -1) && (rhsCounter != -1))
			{
				temp.power = counter;
				temp.coeff = (int)polynomial[lhsCounter].coeff - (int)rhsVector[rhsCounter].coeff;
				if(temp.coeff != 0)
				{
					result.push_back(temp);
				}
			}
			else if((lhsCounter != -1) && (rhsCounter == -1))
			{
				if(temp.coeff != 0)
				{
					result.push_back(polynomial[lhsCounter]);
				}
			}
			else if((rhsCounter != -1) && (lhsCounter == -1))
			{
				rhsVector[rhsCounter].coeff = -rhsVector[rhsCounter].coeff;
				if(temp.coeff != 0)
				{
					result.push_back(rhsVector[rhsCounter]);
				}
			}
		}
		polynomial = result;
		return *this;
	}

	// Fliped signs of all the coefficients
	Poly operator -(const Poly & somePoly) 
	{
		vector<Monomial> temp = somePoly.polynomial;
		for(int i = 0; i < (int)temp.size(); i++)
		{
			temp[i].coeff = -temp[i].coeff;
		}	
		return Poly(temp);
	}

	//Used += to do this 
	Poly operator+(const Poly & lhs, const Poly & rhs)
	{
		Poly sum = lhs;
		sum += rhs;
		return sum;
	}
	//Used -= to do this
	Poly operator-(const Poly & lhs, const Poly & rhs)
	{
		Poly sum = lhs;
		sum -= rhs;
		return sum;
	}
	//Used *= to do this
	Poly operator*(const Poly & lhs, const Poly & rhs)
	{
		Poly result = lhs;
		result *= rhs;
		return result;
	}

	//Checked size then checked each polynomial in each monomial
	bool operator==(const Poly & lhs, const Poly & rhs)
	{
		bool result = true;
		vector<Monomial> lhsVector = lhs.polynomial;
		vector<Monomial> rhsVector = rhs.polynomial;
		if(lhsVector.size() == rhsVector.size())
		{
			for(int i = 0; i < (int)lhsVector.size(); i++)
			{
				if(lhsVector[i].power != rhsVector[i].power)
				{
					result = false;
				}
				if(lhsVector[i].coeff != rhsVector[i].coeff)
				{
					result = false;
				}
			}
		}
		else
		{
			result = false;
		}
		return result;
	}
	
	//Used == and inversed it
	bool operator!=(const Poly & lhs, const Poly & rhs)
	{
		if(lhs == rhs)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	
	//Printed many cases and had to handle them all.. 
	ostream & operator<< (ostream & out, const Poly & value)
	{
		vector<Monomial> tempVector = value.polynomial;
		if (tempVector.size() == 0)
		{
			out << (int)0;
		}
		int placeholder = 0;
		while((int)tempVector.size() > 0)
		{
			Monomial tempMonomial = tempVector.back();
			if((tempMonomial.coeff == 1) || (tempMonomial.coeff == -1))
			{
				if(tempMonomial.power == 0)
				{
					if((placeholder > 0) && (tempMonomial.coeff > 0))
					{
						out << '+';
						out << (int)tempMonomial.coeff;
					}
					else if((placeholder == 0) && (tempMonomial.coeff == -1))
					{
						out << (int)tempMonomial.coeff;
					}
					else{
						out << (int)tempMonomial.coeff;
					}
				}
				else if(tempMonomial.power == 1)
				{	
					if(tempMonomial.coeff > 0){
						if(placeholder > 0)
						{
							out << '+';
						}
						out << 'x';
					}
					else{
						out << '-';
						out << 'x';
					}
				}
				else
				{
					if (tempMonomial.coeff > 0)
					{	
						if(placeholder > 0)
						{
							out << '+';
						}
						out << 'x';
						out << '^';
						out << (int)tempMonomial.power;
					}
					else
					{
						out << '-';
						out << 'x';
						out << '^';
						out << (int)tempMonomial.power;
					}
				}
			}
			else
			{
				if(tempMonomial.power == 0 )
				{
					if((placeholder > 0) && (tempMonomial.coeff > 0))
					{
						out << '+';
					}
					out << (int)tempMonomial.coeff;
				}
				else if(tempMonomial.power == 1)
				{
					if((placeholder > 0) && (tempMonomial.coeff > 0))
					{
						out << '+';
					}
					out << (int)tempMonomial.coeff;
					out << 'x';
				}
				else
				{
					if((placeholder > 0) && (tempMonomial.coeff > 0))
					{
						out << '+';
					}
					out << (int)tempMonomial.coeff;
					out << 'x';
					out << '^';
					out << (int)tempMonomial.power;
				}
			}
			tempVector.pop_back();
			placeholder++;
		}
		
		return out;
	}
}
